"""
管理后台路由模块
实现系统管理相关接口，如用户管理、系统配置、操作日志等
"""

# 添加项目根目录到Python路径
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# 导入FastAPI相关组件
from fastapi import APIRouter, Depends, HTTPException, status, Request
# 导入Pydantic模型用于数据验证
from pydantic import BaseModel
# 导入类型提示
from typing import List, Optional
# 导入日期时间处理模块
from datetime import datetime
# 导入SQLAlchemy会话
from sqlalchemy.orm import Session

# 导入模型和数据库会话
from utils.models import SystemConfig as SystemConfigModel, OperationLog as OperationLogModel, get_db

# 创建API路由实例
router = APIRouter(
    prefix="/api/v1/admin",  # 路由前缀
    tags=["admin"],  # 标签
    responses={404: {"description": "Not found"}},  # 响应定义
)

# 数据模型定义
class SystemConfigBase(BaseModel):
    """
    系统配置基础数据模型
    用于定义系统配置的基本字段结构
    """
    key: str            # 配置键，唯一标识一个配置项
    value: str          # 配置值，配置项的具体内容
    description: Optional[str] = None  # 配置描述，对配置项的说明

class SystemConfigCreate(SystemConfigBase):
    """
    系统配置创建数据模型
    用于创建新系统配置时的数据验证
    继承自SystemConfigBase，字段结构相同
    """

class SystemConfigUpdate(BaseModel):
    """
    系统配置更新数据模型
    用于更新系统配置时的数据验证
    所有字段都是可选的，因为更新时可能只修改部分字段
    """
    value: Optional[str] = None          # 配置值
    description: Optional[str] = None    # 配置描述

class SystemConfig(SystemConfigBase):
    """
    系统配置完整数据模型
    用于返回系统配置的完整信息，包括自动生成的字段
    """
    id: int             # 配置ID，数据库中的主键
    created_at: str     # 创建时间，ISO格式的时间字符串
    updated_at: str     # 更新时间，ISO格式的时间字符串

    class Config:
        from_attributes = True  # 启用ORM模式，允许从ORM模型实例创建Pydantic模型

class SystemConfigResponse(BaseModel):
    """
    系统配置响应数据模型
    用于系统配置相关接口的统一响应格式
    """
    message: str                    # 响应消息，描述操作结果
    config: Optional[SystemConfig] = None  # 系统配置数据，可选字段

class SystemConfigListResponse(BaseModel):
    """
    系统配置列表响应数据模型
    用于返回系统配置列表的统一响应格式
    """
    message: str                  # 响应消息，描述操作结果
    configs: List[SystemConfig]   # 系统配置列表

class OperationLogBase(BaseModel):
    """
    操作日志基础数据模型
    用于定义操作日志的基本字段结构
    """
    user_id: int        # 用户ID，执行操作的用户标识
    username: str       # 用户名，执行操作的用户名称
    operation_type: str # 操作类型，如"GET_REQUEST", "POST_REQUEST"等
    description: str    # 操作描述，对操作的详细说明
    method: Optional[str] = None     # HTTP请求方法，如GET, POST等
    path: Optional[str] = None       # 请求路径
    params: Optional[str] = None     # 请求参数
    status_code: Optional[int] = None # 响应状态码
    ip_address: Optional[str] = None # IP地址，客户端的IP地址
    user_agent: Optional[str] = None # 用户代理，客户端的信息

class OperationLogCreate(OperationLogBase):
    """
    操作日志创建数据模型
    用于创建新操作日志时的数据验证
    继承自OperationLogBase，字段结构相同
    """

class OperationLog(OperationLogBase):
    """
    操作日志完整数据模型
    用于返回操作日志的完整信息，包括自动生成的字段
    """
    id: int             # 日志ID，数据库中的主键
    created_at: str     # 创建时间，ISO格式的时间字符串

    class Config:
        from_attributes = True  # 启用ORM模式，允许从ORM模型实例创建Pydantic模型

class OperationLogResponse(BaseModel):
    """
    操作日志响应数据模型
    用于单个操作日志查询接口的统一响应格式
    """
    message: str                      # 响应消息，描述操作结果
    log: Optional[OperationLog] = None       # 操作日志数据，可选字段

class OperationLogListResponse(BaseModel):
    """
    操作日志列表响应数据模型
    用于操作日志列表查询接口的统一响应格式
    """
    message: str                    # 响应消息，描述操作结果
    logs: List[OperationLog]        # 操作日志列表

class BackupResponse(BaseModel):
    """
    备份响应数据模型
    用于数据库备份接口的统一响应格式
    """
    message: str        # 响应消息，描述操作结果
    backup_path: str    # 备份路径，备份文件的存储路径

class RestoreRequest(BaseModel):
    """
    恢复请求数据模型
    用于数据库恢复接口的请求数据验证
    """
    backup_path: str    # 备份路径，要恢复的备份文件路径

class RestoreResponse(BaseModel):
    """
    恢复响应数据模型
    用于数据库恢复接口的统一响应格式
    """
    message: str        # 响应消息，描述操作结果

# 系统管理服务类
class AdminService:
    """
    系统管理服务类
    提供系统管理相关的业务逻辑处理，包括配置管理、日志处理、数据库备份恢复等
    """
    
    @staticmethod
    def config_to_dict(config: SystemConfigModel) -> dict:
        """
        将数据库系统配置模型转换为字典
        用于将数据库查询结果转换为可序列化的字典格式
        Args:
            config (SystemConfigModel): 数据库系统配置模型实例
        Returns:
            dict: 包含配置信息的字典
        """
        return {
            "id": config.id,
            "key": config.key,
            "value": config.value,
            "description": config.description,
            "created_at": config.created_at.isoformat() if config.created_at else None,
            "updated_at": config.updated_at.isoformat() if config.updated_at else None
        }
    
    @staticmethod
    def log_to_dict(log: OperationLogModel) -> dict:
        """
        将数据库操作日志模型转换为字典
        用于将数据库查询结果转换为可序列化的字典格式
        Args:
            log (OperationLogModel): 数据库操作日志模型实例
        Returns:
            dict: 包含日志信息的字典
        """
        return {
            "id": log.id,
            "user_id": log.user_id,
            "username": log.username,
            "operation_type": log.operation_type,
            "description": log.description,
            "method": log.method,
            "path": log.path,
            "params": log.params,
            "status_code": log.status_code,
            "ip_address": log.ip_address,
            "user_agent": log.user_agent,
            "created_at": log.created_at.isoformat() if log.created_at else None
        }
    
    @staticmethod
    def get_client_ip(request: Request) -> str:
        """
        获取客户端IP地址
        从HTTP请求中提取客户端的真实IP地址
        Args:
            request (Request): HTTP请求对象
        Returns:
            str: 客户端IP地址
        """
        # 尝试从X-Forwarded-For头部获取（适用于使用反向代理的情况）
        forwarded_for = request.headers.get("X-Forwarded-For")
        if forwarded_for:
            # X-Forwarded-For可能包含多个IP地址，取第一个（最原始的客户端IP）
            return forwarded_for.split(",")[0].strip()
        
        # 尝试从X-Real-IP头部获取
        real_ip = request.headers.get("X-Real-IP")
        if real_ip:
            return real_ip
        
        # 从请求客户端信息获取
        return request.client.host if request.client else "unknown"
    
    @staticmethod
    def backup_database() -> str:
        """
        备份数据库
        使用mysqldump命令备份数据库到指定文件
        Returns:
            str: 备份文件的完整路径
        Raises:
            Exception: 备份过程中可能出现的异常
        """
        # 获取数据库配置信息
        mysql_user = os.getenv('MYSQL_USER', 'root')           # MySQL用户名
        mysql_pass = os.getenv('MYSQL_PASS', 'password')       # MySQL密码
        mysql_host = os.getenv('MYSQL_HOST', 'localhost')      # MySQL主机
        mysql_port = os.getenv('MYSQL_PORT', '3306')           # MySQL端口
        mysql_db = os.getenv('MYSQL_DB', 'immune_data_platform')  # MySQL数据库名
        
        # 创建备份目录，如果不存在则创建
        backup_dir = "./backups"
        os.makedirs(backup_dir, exist_ok=True)
        
        # 生成备份文件名，包含数据库名和时间戳
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_filename = f"backup_{mysql_db}_{timestamp}.sql"
        backup_path = os.path.join(backup_dir, backup_filename)
        
        # 构建mysqldump命令参数
        dump_command = [
            "mysqldump",
            f"--user={mysql_user}",
            f"--password={mysql_pass}",
            f"--host={mysql_host}",
            f"--port={mysql_port}",
            mysql_db
        ]
        
        # 执行备份命令，将输出重定向到备份文件
        with open(backup_path, 'w') as backup_file:
            subprocess.run(dump_command, stdout=backup_file, check=True)
        
        return backup_path
    
    @staticmethod
    def restore_database(backup_path: str) -> bool:
        """
        恢复数据库
        使用mysql命令从备份文件恢复数据库
        Args:
            backup_path (str): 备份文件的完整路径
        Returns:
            bool: 恢复是否成功
        Raises:
            Exception: 恢复过程中可能出现的异常
        """
        # 检查备份文件是否存在
        if not os.path.exists(backup_path):
            raise Exception(f"备份文件不存在: {backup_path}")
        
        # 获取数据库配置信息
        mysql_user = os.getenv('MYSQL_USER', 'root')           # MySQL用户名
        mysql_pass = os.getenv('MYSQL_PASS', 'password')       # MySQL密码
        mysql_host = os.getenv('MYSQL_HOST', 'localhost')      # MySQL主机
        mysql_port = os.getenv('MYSQL_PORT', '3306')           # MySQL端口
        mysql_db = os.getenv('MYSQL_DB', 'immune_data_platform')  # MySQL数据库名
        
        # 构建mysql命令参数
        restore_command = [
            "mysql",
            f"--user={mysql_user}",
            f"--password={mysql_pass}",
            f"--host={mysql_host}",
            f"--port={mysql_port}",
            mysql_db
        ]
        
        # 执行恢复命令，从备份文件读取输入
        with open(backup_path, 'r') as backup_file:
            subprocess.run(restore_command, stdin=backup_file, check=True)
        
        return True

# 路由定义
@router.get("/configs/", response_model=SystemConfigListResponse)
async def list_configs(db: Session = Depends(get_db)):
    """
    获取系统配置列表接口
    从数据库获取所有系统配置项的列表，支持分页
    Args:
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        SystemConfigListResponse: 系统配置列表响应
    """
    try:
        # 从数据库获取所有配置项
        configs = db.query(SystemConfigModel).all()
        
        # 转换为响应格式
        config_list = [SystemConfig(**AdminService.config_to_dict(c)) for c in configs]
        
        return {
            "message": "系统配置列表获取成功",
            "configs": config_list
        }
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取系统配置列表失败: {str(e)}"
        )

@router.post("/configs/", response_model=SystemConfigResponse)
async def create_config(config: SystemConfigCreate, db: Session = Depends(get_db)):
    """
    创建系统配置接口
    将新的系统配置项保存到数据库
    Args:
        config (SystemConfigCreate): 系统配置创建信息
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        SystemConfigResponse: 系统配置响应
    """
    try:
        # 检查配置键是否已存在，避免重复创建
        existing_config = db.query(SystemConfigModel).filter(SystemConfigModel.key == config.key).first()
        if existing_config:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"配置键 '{config.key}' 已存在"
            )
        
        # 创建配置记录
        db_config = SystemConfigModel(
            key=config.key,
            value=config.value,
            description=config.description
        )
        
        # 保存到数据库
        db.add(db_config)
        db.commit()
        db.refresh(db_config)
        
        # 转换为响应格式
        config_dict = AdminService.config_to_dict(db_config)
        
        return {
            "message": "系统配置创建成功",
            "config": SystemConfig(**config_dict)
        }
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"创建系统配置失败: {str(e)}"
        )

@router.get("/configs/{config_key}", response_model=SystemConfigResponse)
async def get_config(config_key: str, db: Session = Depends(get_db)):
    """
    获取系统配置详情接口
    从数据库获取指定系统配置项的详细信息
    Args:
        config_key (str): 配置键
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        SystemConfigResponse: 系统配置响应
    Raises:
        HTTPException: 当配置未找到时抛出404异常
    """
    try:
        # 从数据库获取配置
        db_config = db.query(SystemConfigModel).filter(SystemConfigModel.key == config_key).first()
        
        # 检查配置是否存在
        if not db_config:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"系统配置 '{config_key}' 未找到"
            )
        
        # 转换为响应格式
        config_dict = AdminService.config_to_dict(db_config)
        
        return {
            "message": "系统配置获取成功",
            "config": SystemConfig(**config_dict)
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取系统配置失败: {str(e)}"
        )

@router.put("/configs/{config_key}", response_model=SystemConfigResponse)
async def update_config(config_key: str, config: SystemConfigUpdate, db: Session = Depends(get_db)):
    """
    更新系统配置接口
    更新数据库中的系统配置信息
    Args:
        config_key (str): 配置键
        config (SystemConfigUpdate): 系统配置更新信息
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        SystemConfigResponse: 系统配置响应
    Raises:
        HTTPException: 当配置未找到时抛出404异常
    """
    try:
        # 从数据库获取配置
        db_config = db.query(SystemConfigModel).filter(SystemConfigModel.key == config_key).first()
        
        # 检查配置是否存在
        if not db_config:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"系统配置 '{config_key}' 未找到"
            )
        
        # 更新配置信息
        if config.value is not None:
            db_config.value = config.value
        if config.description is not None:
            db_config.description = config.description
        
        # 更新时间自动更新（由模型定义）
        
        # 保存到数据库
        db.commit()
        db.refresh(db_config)
        
        # 转换为响应格式
        config_dict = AdminService.config_to_dict(db_config)
        
        return {
            "message": "系统配置更新成功",
            "config": SystemConfig(**config_dict)
        }
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"更新系统配置失败: {str(e)}"
        )

@router.delete("/configs/{config_key}")
async def delete_config(config_key: str, db: Session = Depends(get_db)):
    """
    删除系统配置接口
    从数据库中删除指定的系统配置
    Args:
        config_key (str): 配置键
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        dict: 删除成功信息
    Raises:
        HTTPException: 当配置未找到时抛出404异常
    """
    try:
        # 从数据库获取配置
        db_config = db.query(SystemConfigModel).filter(SystemConfigModel.key == config_key).first()
        
        # 检查配置是否存在
        if not db_config:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"系统配置 '{config_key}' 未找到"
            )
        
        # 从数据库删除配置
        db.delete(db_config)
        db.commit()
        
        return {"message": f"系统配置 '{config_key}' 删除成功"}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"删除系统配置失败: {str(e)}"
        )

@router.get("/logs/", response_model=OperationLogListResponse)
async def list_logs(
    skip: int = 0,
    limit: int = 100,
    user_id: Optional[int] = None,
    operation_type: Optional[str] = None,
    db: Session = Depends(get_db)
):
    """
    获取操作日志列表接口
    从数据库获取操作日志列表，支持分页和筛选
    Args:
        skip (int): 跳过的记录数，用于分页
        limit (int): 限制返回的记录数，用于分页
        user_id (int): 用户ID筛选，只返回指定用户的操作日志
        operation_type (str): 操作类型筛选，只返回指定类型的操作日志
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        OperationLogListResponse: 操作日志列表响应
    """
    try:
        # 构建查询
        query = db.query(OperationLogModel)
        
        # 添加用户ID筛选条件
        if user_id is not None:
            query = query.filter(OperationLogModel.user_id == user_id)
        
        # 添加操作类型筛选条件
        if operation_type is not None:
            query = query.filter(OperationLogModel.operation_type == operation_type)
        
        # 从数据库获取日志列表，按创建时间倒序排列，支持分页
        logs = query.order_by(OperationLogModel.created_at.desc()).offset(skip).limit(limit).all()
        
        # 转换为响应格式
        log_list = [OperationLog(**AdminService.log_to_dict(l)) for l in logs]
        
        return {
            "message": "操作日志列表获取成功",
            "logs": log_list
        }
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取操作日志列表失败: {str(e)}"
        )

@router.get("/logs/{log_id}", response_model=OperationLogResponse)
async def get_log(log_id: int, db: Session = Depends(get_db)):
    """
    获取操作日志详情接口
    从数据库获取指定操作日志的详细信息
    Args:
        log_id (int): 日志ID
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        OperationLogResponse: 操作日志响应
    Raises:
        HTTPException: 当日志未找到时抛出404异常
    """
    try:
        # 从数据库获取日志
        db_log = db.query(OperationLogModel).filter(OperationLogModel.id == log_id).first()
        
        # 检查日志是否存在
        if not db_log:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"操作日志 {log_id} 未找到"
            )
        
        # 转换为响应格式
        log_dict = AdminService.log_to_dict(db_log)
        
        return {
            "message": "操作日志获取成功",
            "log": OperationLog(**log_dict)
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取操作日志失败: {str(e)}"
        )

@router.delete("/logs/{log_id}")
async def delete_log(log_id: int, db: Session = Depends(get_db)):
    """
    删除操作日志接口
    从数据库中删除指定的操作日志
    Args:
        log_id (int): 日志ID
        db (Session): 数据库会话，通过依赖注入获取
    Returns:
        dict: 删除成功信息
    Raises:
        HTTPException: 当日志未找到时抛出404异常
    """
    try:
        # 从数据库获取日志
        db_log = db.query(OperationLogModel).filter(OperationLogModel.id == log_id).first()
        
        # 检查日志是否存在
        if not db_log:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"操作日志 {log_id} 未找到"
            )
        
        # 从数据库删除日志
        db.delete(db_log)
        db.commit()
        
        return {"message": f"操作日志 {log_id} 删除成功"}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"删除操作日志失败: {str(e)}"
        )

@router.post("/backup", response_model=BackupResponse)
async def backup_database():
    """
    数据库备份接口
    执行数据库备份操作，生成SQL格式的备份文件
    Returns:
        BackupResponse: 备份响应
    """
    try:
        # 执行数据库备份
        backup_path = AdminService.backup_database()
        
        return {
            "message": "数据库备份成功",
            "backup_path": backup_path
        }
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"数据库备份失败: {str(e)}"
        )

@router.post("/restore", response_model=RestoreResponse)
async def restore_database(request: RestoreRequest):
    """
    数据库恢复接口
    执行数据库恢复操作，从指定的备份文件恢复数据
    Args:
        request (RestoreRequest): 恢复请求，包含备份文件路径
    Returns:
        RestoreResponse: 恢复响应
    """
    try:
        # 执行数据库恢复
        AdminService.restore_database(request.backup_path)
        
        return {
            "message": "数据库恢复成功"
        }
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"数据库恢复失败: {str(e)}"
        )